package com.isyscore.os.metadata.service.diop;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.isyscore.boot.login.LoginUserManager;
import com.isyscore.device.common.util.JsonMapper;
import com.isyscore.os.core.entity.SqlQuery;
import com.isyscore.os.core.entity.SqlQueryGroup;
import com.isyscore.os.core.exception.DataFactoryException;
import com.isyscore.os.core.exception.ErrorCode;
import com.isyscore.os.metadata.model.entity.*;
import com.isyscore.os.metadata.service.DataSourceService;
import com.isyscore.os.metadata.service.SqlQueryGroupService;
import com.isyscore.os.metadata.service.SqlQueryService;
import com.isyscore.os.metadata.service.impl.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import static com.isyscore.os.metadata.service.diop.DiopConstant.*;

/**
 * @author wany
 */
@Service
@Slf4j
@Transactional
public class ImportService {

    public static final String TENANT_ID = "tenantId";
    public static final String DEFAULT_TENANT_ID = "initially";
    @Autowired
    private DatamodelService datamodelService;

    @Autowired
    private ErRelationService erRelationService;

    @Autowired
    private DatamodelColumnService datamodelColumnService;

    @Autowired
    private SqlStatementService sqlStatementService;

    @Autowired
    private MetricService metricService;

    @Autowired
    private MetricGroupService metricGroupService;

    @Autowired
    private MetricDimFilterService metricDimFilterService;

    @Autowired
    private MetricJobService metricJobService;

    @Autowired
    private DataSourceService dataSourceService;

    @Autowired
    private MetricMeasureService metricMeasureService;
    @Autowired
    private SqlQueryService sqlQueryService;

    @Autowired
    private SqlQueryGroupService sqlQueryGroupService;

    @Autowired
    private LoginUserManager loginUserManager;

    public void doImportResource(String dirPath) {
        String datamodelFilesDir = dirPath + File.separator + METRIC_RESOURCE_NAME;
        String metricFilesDir = dirPath + File.separator + DATAMODEL_RESOURCE_NAME;
        String sqlQueryFileDir = dirPath + File.separator + SQL_QUERY_RESOURCE_NAME;
        List<File> dataFiles = Lists.newArrayList();
        if (FileUtil.exist(datamodelFilesDir)) {
            dataFiles.addAll(Arrays.asList(FileUtil.ls(datamodelFilesDir)));
        }
        if (FileUtil.exist(metricFilesDir)) {
            dataFiles.addAll(Arrays.asList(FileUtil.ls(metricFilesDir)));
        }
        if (FileUtil.exist(sqlQueryFileDir)) {
            dataFiles.addAll(Arrays.asList(FileUtil.ls(sqlQueryFileDir)));
        }
        List<Metric> metrics = null;
        for (File dataFile : dataFiles) {
            String nameWithoutSuffix = removeFileNameSuffix(dataFile.getName());
            switch (nameWithoutSuffix) {
                case DATAMODEL_RESOURCE_NAME:
                    List<Datamodel> datamodels = this.parseResourceObject(dataFile, new TypeReference<List<Datamodel>>() {
                    });
//                    checkTenantId(datamodels);
                    datamodelService.importData(datamodels);
                    break;
                case METRIC_FILE_NAME:
                    metrics = this.parseResourceObject(dataFile, new TypeReference<List<Metric>>() {
                    });
//                    checkTenantId(metrics);
                    metricService.importData(metrics);
                    break;
                case DATAMODEL_COLUMN_FILE_NAME: {
                    List<DatamodelColumn> datas = this.parseResourceObject(dataFile, new TypeReference<List<DatamodelColumn>>() {
                    });
//                    checkTenantId(datas);
                    datamodelColumnService.importData(datas);
                    break;
                }
                case ER_RELATION_FILE_NAME: {
                    List<ErRelation> datas = this.parseResourceObject(dataFile, new TypeReference<List<ErRelation>>() {
                    });
//                    checkTenantId(datas);
                    erRelationService.importData(datas);
                    break;
                }
                case SQL_FILE_NAME: {
                    List<SqlStatement> datas = this.parseResourceObject(dataFile, new TypeReference<List<SqlStatement>>() {
                    });
//                    checkTenantId(datas);
                    sqlStatementService.importData(datas);
                    break;
                }
                case METRIC_MEASURE_FILE_NAME: {
                    List<MetricMeasure> datas = this.parseResourceObject(dataFile, new TypeReference<List<MetricMeasure>>() {
                    });
//                    checkTenantId(datas);
                    metricMeasureService.importData(datas);
                    break;
                }
                case METRIC_GROUP_FILE_NAME: {
                    List<MetricGroup> datas = this.parseResourceObject(dataFile, new TypeReference<List<MetricGroup>>() {
                    });
//                    checkTenantId(datas);
                    metricGroupService.importData(datas);
                    break;
                }
                case METRIC_DIM_FILTER_FILE_NAME: {
                    List<MetricDimFilter> datas = this.parseResourceObject(dataFile, new TypeReference<List<MetricDimFilter>>() {
                    });
//                    checkTenantId(datas);
                    metricDimFilterService.importData(datas);
                    break;
                }
                case DATASOURCE_FILE_NAME: {
                    List<DataSource> datas = this.parseResourceObject(dataFile, new TypeReference<List<DataSource>>() {
                    });
//                    checkTenantId(datas);
                    dataSourceService.importDataSources(datas);
                    break;
                }
                case SQL_QUERY_FILE_NAME: {
                    List<SqlQuery> datas = this.parseResourceObject(dataFile, new TypeReference<List<SqlQuery>>() {
                    });
//                    checkTenantId(datas);
                    sqlQueryService.importData(datas);
                    break;
                }
                case SQL_QUERY_GROUP_FILE_NAME: {
                    List<SqlQueryGroup> datas = this.parseResourceObject(dataFile, new TypeReference<List<SqlQueryGroup>>() {
                    });
//                    checkTenantId(datas);
                    sqlQueryGroupService.importData(datas);
                    break;
                }
                default:
                    break;
            }
            //所有导入完成后刷新导入指标的定时任务信息
            if (metrics != null) {
                for (Metric metric : metrics) {
                    if (!Strings.isNullOrEmpty(metric.getTaskCron())) {
                        metricJobService.refreshMetricJob(metric.getId(), metric.getTaskCron());
                    } else {
                        metricJobService.deleteMetricJob(metric.getId());
                    }
                }
            }
        }
    }

    private String removeFileNameSuffix(String fileName) {
        if (Strings.isNullOrEmpty(fileName)) {
            return "";
        }
        return fileName.substring(0, fileName.length() - ".json".length());
    }

    public <T> T parseResourceObject(File dataFile, TypeReference<T> typeReference) {
        String jsonContent = FileUtil.readString(dataFile, "utf-8");
        return JsonMapper.fromJson(jsonContent, typeReference);
    }

    /**
     * checkTenantId 检查数据租户id
     *
     * @param data 需要检查的数据，如果tenantId与登录用户的id或者初始租户不匹配，则抛异常
     */
    @SneakyThrows
    private <T> void checkTenantId(List<T> data) {
        if (!data.stream().allMatch(x -> (StrUtil.equalsAny(String.valueOf(BeanUtil.getFieldValue(x, TENANT_ID)), loginUserManager.getCurrentTenantId(), DEFAULT_TENANT_ID)))) {
            throw new DataFactoryException(ErrorCode.TENANT_INFO_ERROR);
        }
    }
}
